#ifndef __ENCODE_H
#define __ENCODE_H

#include "./base.h"

namespace serialize {

template <typename T> inline void encode_int(uint8_t *buf, T value) {
    static_assert(is_int<T>, "invalid type");
    if constexpr (is_8bit<T>) {
        buf[0] = static_cast<uint8_t>(value);
    } else if constexpr (is_16bit<T>) {
        buf[0] = static_cast<uint8_t>(value >> 8);
        buf[1] = static_cast<uint8_t>(value);
    } else if constexpr (is_32bit<T>) {
        buf[0] = static_cast<uint8_t>(value >> 24);
        buf[1] = static_cast<uint8_t>(value >> 16);
        buf[2] = static_cast<uint8_t>(value >> 8);
        buf[3] = static_cast<uint8_t>(value);
    }
}

template <typename T, typename... Ts>
inline uint8_t encode_all(uint8_t *buf, const T &value, const Ts &...ts) {
    using TYPE = std::decay_t<T>;
    static_assert(is_int<TYPE> || is_error<TYPE> || is_byte_view<TYPE>,
                  "invalid types");

    if constexpr (is_int<T>) {
        constexpr uint8_t LEN = sizeof(TYPE);
        encode_int<TYPE>(buf, value);
        if constexpr (sizeof...(Ts) == 0) {
            return LEN;
        } else {
            return LEN + encode_all(buf + LEN, ts...);
        }
    } else if constexpr (is_error<T>) {
        constexpr uint8_t LEN = 1;
        encode_int<uint8_t>(buf, value.err_code);
        if constexpr (sizeof...(Ts) == 0) {
            return LEN;
        } else {
            return LEN + encode_all(buf + LEN, ts...);
        }
    } else if constexpr (is_byte_view<T>) {
        uint8_t len = 1 + value.len;
        encode_int<uint8_t>(buf, value.len);
        memcpy(buf + 1, value.buf, len);
        if constexpr (sizeof...(Ts) == 0) {
            return len;
        } else {
            return len + encode_all(buf + len, ts...);
        }
    }
}

} // namespace serialize

#endif
